package com.kedacom.ctsp.authz.oauth2.service;

import com.google.common.collect.Sets;
import com.kedacom.ctsp.authority.entity.Department;
import com.kedacom.ctsp.authority.entity.ResourceSettingRelation;
import com.kedacom.ctsp.authority.entity.Role;
import com.kedacom.ctsp.authority.entity.User;
import com.kedacom.ctsp.authority.service.DepartmentService;
import com.kedacom.ctsp.authority.service.ResourceSettingRelationService;
import com.kedacom.ctsp.authority.service.RoleService;
import com.kedacom.ctsp.authority.service.UserService;
import com.kedacom.ctsp.authz.entity.*;
import com.kedacom.ctsp.authz.exception.UserLoginStatusIllegalException;
import com.kedacom.ctsp.authz.exception.UsernameNotFoundException;
import com.kedacom.ctsp.authz.oauth2.core.OAuth2Authentication;
import com.kedacom.ctsp.authz.security.provider.UserBean;
import com.kedacom.ctsp.lang.mapper.BeanMapper;
import com.kedacom.ctsp.lang.validate.ValidationException;
import com.kedacom.ctsp.web.service.StatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.kedacom.ctsp.authority.entity.SettingForType.*;
import static com.kedacom.ctsp.authz.entity.ResourceEnum.DEPARTMENT_OF_CHARGE;

/**
 * 获取client 用户信息
 *
 * @com.kedacom.ctsp.authz.oauth2.service
 * @fenghaiju
 * @2018/5/18-11:07
 * @cloud-rbac-oauth2
 **/
@Slf4j
@Service
public class OAuth2AuthenticationServiceImpl implements OAuth2AuthenticationService {

    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private ResourceSettingRelationService resourceSettingRelationService;
    @Autowired
    private DepartmentService departmentService;

    @Override
    public OAuth2Authentication loadUserByUsername(String username, String clientId) throws UsernameNotFoundException {

        log.info("loadUserByUsername [{}]", username);
        User user = userService.selectByUsername(username);
        // 用户不存在
        if (user == null) {
            throw new UsernameNotFoundException();
        }
        checkUserAndDeptCorrect(user);
        // 用户状态被禁用
        if (!userService.checkLoginUserStatus(user)) {
            throw new UserLoginStatusIllegalException();
        }
        return initUserAuthorization(user, clientId);

    }

    /**
     * 初始化用户的权限信息
     *
     * @param user
     * @return
     */
    private OAuth2Authentication initUserAuthorization(User user, String clientSign) {
        log.info("initUserAuthorization ");
        OAuth2Authentication authentication = new OAuth2Authentication();
        // 用户信息
        AuthUser authUser = new AuthUser();
        authUser.setId(user.getId());
        authUser.setName(user.getName());
        authUser.setUsername(user.getUsername());
        authentication.setUser(authUser);
        authentication.setPassword(user.getPassword());
        log.info("组装user id: {}, username: {}", authentication.getUser().getId(), authentication.getUser().getUsername());

        // 人员信息
        AuthPerson person = userService.getAuthPersonByUser(authentication.getUser());
        AuthPerson simpleAuthPerson = new AuthPerson();
        BeanMapper.deepCopy(person, simpleAuthPerson);
        authentication.setPerson(simpleAuthPerson);
        if (authentication.getPerson() != null) {
            log.info("组装person id: {}, name: {}", authentication.getPerson().getId(), authentication.getPerson().getName());
        }

        // 部门
        String departmentId = null;
        if (authentication.getPerson() != null) {
            String deptCode = authentication.getPerson().getDeptCode();
            Department department = departmentService.selectByCode(deptCode);
            if (department != null) {
                departmentId = department.getId();
            }
            log.info("部门 departmentId: {} ", departmentId);
        }

        log.info("查询角色 userId: {} , departmentId", user.getId(), departmentId);
        Set<AuthRole> roles = getAuthRolesByUidAndDid(user.getId(), departmentId, clientSign);
        // 角色
        Set<AuthRole> authRoles = roles.stream().map(r -> {
            AuthRole authRole = new AuthRole();
            BeanMapper.deepCopy(r, authRole);
            return authRole;
        }).collect(Collectors.toSet());

        authentication.setRoles(authRoles);
        log.info("查询到角色: " + roles.size());

        Set<String> roleIds = roles.stream().map(AuthRole::getId).collect(Collectors.toSet());

        log.info("查询用户资源: userId: {}, departmentId: {}, roles: {}, : 资源来源: {}", authentication.getUser().getId(),
                departmentId, roleIds, ResourceEnum.SELF.ordinal());
        //用户资源
        Set<AuthResource> userSelfResources = getAuthResourcesByClient(
                authentication.getUser().getId(), departmentId, roleIds, ResourceEnum.SELF, clientSign);

        Set<AuthResource> simpleAuthResources = userSelfResources.stream().map(r -> {
            AuthResource simpleAuthResource = new AuthResource();
            BeanMapper.deepCopy(r, simpleAuthResource);
            return simpleAuthResource;
        }).collect(Collectors.toSet());


        // 分管用户的资源
        Set<AuthResource> authResourcesOfChargedUsers = getAuthResourcesOfChargedUsers(person);
        userSelfResources.addAll(authResourcesOfChargedUsers);

        // 分管部门的资源
        if (person != null) {
            Set<String> deptIdsInCharge = person.getCascadedAndChargedDeptCodes();
            if (CollectionUtils.isNotEmpty(deptIdsInCharge)) {
                Set<AuthResource> allDeptChargeResources = resourceSettingRelationService.getAuthResourcesByDids(deptIdsInCharge, DEPARTMENT_OF_CHARGE);
                userSelfResources.addAll(allDeptChargeResources);
            }
        }
        //TODO
        // authentication.setResources(simpleAuthResources);
        log.info("initUserAuthorization return");
        return authentication;
    }

    private Set<AuthResource> getAuthResourcesOfChargedUsers(AuthPerson person) {
        if (person != null) {
            Set<String> peopleOfCharge = person.getChargedUsers();
            if (CollectionUtils.isNotEmpty(peopleOfCharge)) {
                Set<AuthResource> allUserOfChargeResources = peopleOfCharge.stream().map(
                        uid -> {
                            Set<String> authRoleIds = userService.getAuthRolesByUser(uid).stream().map(AuthRole::getId).collect(Collectors.toSet());
                            String deptId = userService.getDeptIdByUserId(uid);
                            return resourceSettingRelationService.getAuthResourcesByRoles(uid, deptId, authRoleIds, ResourceEnum.USER_OF_CHARGE);
                        })
                        .flatMap(Set::stream)
                        .collect(Collectors.toSet());
                return allUserOfChargeResources;
            }
        }
        return Sets.newHashSet();
    }

    /**
     * 获取角色（用户角色+部门角色）
     *
     * @param uid
     * @param deptId
     * @param clientSign
     * @return
     */
    public Set<AuthRole> getAuthRolesByUidAndDid(String uid, String deptId, String clientSign) {
        Set<AuthRole> authRolesUser = this.getAuthRolesByUser(uid, clientSign);
        log.info("用户的角色: " + authRolesUser.size());
        if (deptId != null) {
            Set<Role> authRolesDept = departmentService.get(deptId).getRoles();
            authRolesDept = authRolesDept.stream().filter(r -> {
                //判断clientSign是否为空
                if (r.getClientSign() == null) {
                    return false;
                }
                //只取client相关功能
                if (!r.getClientSign().equalsIgnoreCase(clientSign)) {
                    return false;
                }
                return StatusEnum.ENABLE.ordinal() == r.getStatus();
            }).collect(Collectors.toSet());
            log.info("部门的角色: " + authRolesDept.size());
            Set<AuthRole> authRoleSet = new HashSet<>();
            authRolesDept.stream().forEach(role -> {
                AuthRole r = new AuthRole();
                r.setId(role.getId());
                r.setName(role.getName());
                r.setSign(role.getSign());
                authRoleSet.add(r);
            });

            authRolesUser.addAll(authRoleSet);
        }
        return authRolesUser;
    }

    public Set<AuthRole> getAuthRolesByUser(String uid, String clientSign) {
        Set<Role> roles = userService.get(uid).getRoles();
        if (CollectionUtils.isEmpty(roles)) {
            return Sets.newHashSet();
        }
        roles = roles
                .stream()
                //过滤无效的角色
                .filter(r -> {
                    if (r.getStatus() == null) {
                        return false;
                    }

                    // 判断是否属于该client
                    if (r.getClientSign() == null) {
                        return false;
                    }
                    if (!r.getClientSign().equalsIgnoreCase(clientSign)) {
                        return false;
                    }
                    return StatusEnum.ENABLE.ordinal() == r.getStatus();
                }).collect(Collectors.toSet());

        Set<AuthRole> authRoles = new HashSet<>();
        roles.stream().forEach(role -> {
            AuthRole r = new AuthRole();
            r.setId(role.getId());
            r.setName(role.getName());
            r.setSign(role.getSign());
            authRoles.add(r);
        });
        return authRoles;
    }

    /***
     * 获取资源信息
     * @param uid
     * @param deptId
     * @param roleIds
     * @param resourceEnum
     * @return
     */
    public Set<AuthResource> getAuthResourcesByClient(String uid, String deptId, Set<String> roleIds, ResourceEnum resourceEnum, String clientSign) {

        log.info("initUserResources userId:[{}]", uid);

        Set<AuthResource> resources = Sets.newHashSet();

        //用户资源
        Set<AuthResource> userResources = getAuthResourcesByUser(uid, resourceEnum, clientSign);
        log.info("用户资源: " + userResources.size());
        resources.addAll(userResources);

        //角色资源
        if (CollectionUtils.isNotEmpty(roleIds)) {
            Set<AuthResource> roleResources = getAuthResourcesByRoles(roleIds, resourceEnum, clientSign);
            log.info("角色资源: " + roleResources.size());
            resources.addAll(roleResources);
        }
        // 部门资源
        if (deptId != null) {
            Set<AuthResource> deptResources = getAuthResourcesByDid(deptId, resourceEnum, clientSign);
            log.info("部门资源: " + deptResources.size());
            resources.addAll(deptResources);
        }
        return resources;
    }


    /**
     * 获取用户client资源
     *
     * @param userId
     * @param source
     * @return
     */
    public Set<AuthResource> getAuthResourcesByUser(String userId, ResourceEnum source, String clientSign) {
        // user上的resource
        List<ResourceSettingRelation> userRes = resourceSettingRelationService.createQuery().where()
                .is(ResourceSettingRelation.SETTING_FOR_TYPE, USER)
                .is(ResourceSettingRelation.SETTING_FOR_ID, userId)
                .is(ResourceSettingRelation.STATUS, StatusEnum.ENABLE.ordinal())
                .is(ResourceSettingRelation.RESOURCE_CLIENT_SIGN, clientSign)
                .listNoPaging();

        if (CollectionUtils.isNotEmpty(userRes)) {
            return resourceSettingRelationService.convertResources(userRes, source);
        }
        return Sets.newHashSet();
    }

    /***
     * Client角色获取资源
     * @param roleIds
     * @param configSource
     * @return
     */
    public Set<AuthResource> getAuthResourcesByRoles(Set<String> roleIds, ResourceEnum configSource, String clientSign) {
        List<ResourceSettingRelation> resourceSRs = resourceSettingRelationService.createQuery().where()
                .is(ResourceSettingRelation.SETTING_FOR_TYPE, ROLE)
                .in(ResourceSettingRelation.SETTING_FOR_ID, roleIds)
                .is(ResourceSettingRelation.STATUS, StatusEnum.ENABLE.ordinal())
                .is(ResourceSettingRelation.RESOURCE_CLIENT_SIGN, clientSign)
                .listNoPaging();

        if (CollectionUtils.isNotEmpty(resourceSRs)) {
            return resourceSettingRelationService.convertResources(resourceSRs, configSource);
        }
        return Sets.newHashSet();
    }

    /***
     * Client获取所在部门资源
     * @param deptId
     * @param configSource
     * @param clientSign
     * @return
     */
    public Set<AuthResource> getAuthResourcesByDid(String deptId, ResourceEnum configSource, String clientSign) {

        // 状态是1（启用状态）
        List<ResourceSettingRelation> rss = resourceSettingRelationService.createQuery().where().is(
                ResourceSettingRelation.SETTING_FOR_TYPE, DEPARTMENT
        ).is(ResourceSettingRelation.SETTING_FOR_ID, deptId)
                .is(ResourceSettingRelation.STATUS, StatusEnum.ENABLE.ordinal())
                .is(ResourceSettingRelation.RESOURCE_CLIENT_SIGN, clientSign)
                .listNoPaging();

        if (CollectionUtils.isNotEmpty(rss)) {
            return resourceSettingRelationService.convertResources(rss, configSource);
        }
        return Sets.newHashSet();
    }

    /**
     * 检测当前部门是否正确
     *
     * @param user
     */
    private void checkUserAndDeptCorrect(User user) {
        log.info("判断用户是否在某个部门下");
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        UserBean userBean = (UserBean) httpServletRequest.getAttribute("authUser");
        if (userBean != null && StringUtils.isNotBlank(userBean.getDepartmentCode())) {
            if (user.getPerson() != null && user.getPerson().getDepartment() != null && StringUtils.isNotBlank(user.getPerson().getDepartment().getCode())) {
                if (!userBean.getDepartmentCode().equals(user.getPerson().getDepartment().getCode())) {
                    throw new ValidationException("当前上传的部门编号" + userBean.getDepartmentCode() + ",与用户实际的部门编号不符" + user.getPerson().getDepartment().getCode());
                }
            }
        }
    }
}
